package com.gome.ocean.controller.gddl.app.group;

import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.List;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.github.pagehelper.Page;
import com.gome.ocean.common.constants.DataxContants;
import com.gome.ocean.common.utils.ResponsePage;
import com.gome.ocean.dao.model.datasource.DatasourceBO;
import com.gome.ocean.dao.model.diamond.ConfigInfoBO;
import com.gome.ocean.dao.model.gddl.AppBO;
import com.gome.ocean.dao.model.gddl.AppDatasourceBO;
import com.gome.ocean.dao.model.gddl.GroupBO;
import com.gome.ocean.dao.model.gddl.GroupDllVO;
import com.gome.ocean.dao.model.gddl.GroupRuleBO;
import com.gome.ocean.dao.model.gddl.GroupRuleVO;
import com.gome.ocean.service.base.AuthorityBaseController;
import com.gome.ocean.service.datax.DatasourceService;
import com.gome.ocean.service.diamond.ConfigInfoService;
import com.gome.ocean.service.gddl.AppDatasourceService;
import com.gome.ocean.service.gddl.AppService;
import com.gome.ocean.service.gddl.GddlShardAppService;
import com.gome.ocean.service.gddl.GroupRuleService;
import com.gome.ocean.service.gddl.GroupService;
import com.google.common.collect.Lists;
import com.taobao.tddl.atom.common.TAtomConstants;
import com.taobao.tddl.atom.config.TAtomConfParser;
import com.taobao.tddl.common.utils.TStringUtil;
import com.taobao.tddl.config.ConfigDataHandlerFactory;
import com.taobao.tddl.group.config.Weight;
import com.taobao.tddl.group.jdbc.TGroupDataSource;

/**
 * Created by liuqingxia on 2016/3/24.
 */
@Controller
@RequestMapping("/tddl/grop")
public class GroupController extends AuthorityBaseController {

    private static final Logger    LOG = LoggerFactory.getLogger(GroupController.class);
    @Autowired
    private GroupService           groupService;
    @Autowired
    protected ConfigInfoService    configInfoService;
    @Autowired
    private GddlShardAppService    gddlShardAppService;
    @Autowired
    protected AppDatasourceService appDatasourceService;
    @Autowired
    protected DatasourceService    datasourceService;
    @Autowired
    protected GroupRuleService     groupRuleService;
    @Autowired
    private AppService             appService;

    protected volatile ConfigDataHandlerFactory configDataHandlerFactory;
    private static final MessageFormat          DBGROUPS_FORMAT = new MessageFormat("com.taobao.tddl.v1_{0}_dbgroups");

    @RequestMapping(value = "/findByAppId", method = RequestMethod.POST)
    @ResponseBody
    public ResponsePage findByAppId(long appId) {
        ResponsePage response = getResponsePage();
        List<GroupBO> groupBOs;
        try {
            groupBOs = groupService.findGroupBOByAppId(appId);
            if (CollectionUtils.isEmpty(groupBOs)) return response.emptyPage(FIND_NO_RESULTS);
            return response.setJustList(groupBOs, FIND_SUCCESS);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return response.setErrorMsg(EXCEPTION_MSG);
        }
    }

    // 跳转
    @RequestMapping(value = "/toSaveGroupPage", method = RequestMethod.GET)
    public ModelAndView toSaveGroupPage(ModelAndView mav, HttpServletRequest request) {
        List<String> projectNames = null;
        try {
            if (isAdmin()) {
                projectNames = getAllProjectNames();
            } else {
                projectNames = getCurrentUserProjects(getHttpRequest());
            }
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            mav.getModel().put("errorMsg", e.getMessage());
        }
        mav.getModel().put("projectNames", projectNames);
        mav.setViewName("gddl/group/insert");
        return mav;
    }

    @RequestMapping(value = "/saveGroup", method = RequestMethod.POST)
    @ResponseBody
    public ResponsePage saveGroup(ResponsePage page, HttpServletRequest request, GroupDllVO groupDllVO) {

        // STEP1:验证非空
        if (StringUtils.isEmpty(groupDllVO.getGroupName()) || "false".equals(groupDllVO.getDatabaseList())) {
            return page.setErrorMsg("数据参数有误，请检查");
        }
        // STEP2:查看group是否已经存在
        if (checkGroupExists(groupDllVO.getVersion(), groupDllVO.getAppName())) {
            return page.setErrorMsg("数据参数有误，请检查");
        }
        if (checkAppExists(groupDllVO.getAppName())) {
            return page.setErrorMsg("应用名重复，请检查");
        }
        // STEP3:录入信息并发消息给diamond
        try {
            if (insertGroupInfo(groupDllVO)) {
                return page.setSuccessMsg("新建读写分离成功");
            } else {
                return page.setErrorMsg("新建读写分离应用时发生异常，请联系管理员");
            }

        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return page.setErrorMsg("新建读写分离应用时发生异常，请联系管理员");
        }

    }

    public boolean checkGroupExists(String version, String appName) {
        ConfigInfoBO configInfoBO = configInfoService
            .findConfigInfoBOByDataId(DataxContants.getTddlGroupPrefix(version, appName));
        return configInfoBO != null;
    }

    public boolean checkAppExists(String appName) {
        try {
            AppBO appBO = appService.findAppBOByAppName(appName);
            if (appBO == null) {
                return Boolean.FALSE;
            } else {
                return Boolean.TRUE;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            LOG.error(e.getMessage());
            return Boolean.TRUE;
        }
    }

    public boolean insertGroupInfo(GroupDllVO groupDllVO) {
        boolean result = gddlShardAppService.createGddlGroupApp(groupDllVO);// diamond信息
        if (result) {
            AppBO appBO = new AppBO();
            appBO.setAppName(groupDllVO.getAppName());
            appBO.setInit(0);
            appBO.setProject(groupDllVO.getProjectName());
            appService.insertAppBO(appBO);
            return Boolean.TRUE;
        } else {
            return Boolean.FALSE;
        }
    }

    @RequestMapping(value = "/toFindPage", method = RequestMethod.GET)
    public ModelAndView toFindPage(ModelAndView mav) {
        mav.setViewName("gddl/group/find");
        return mav;
    }

    @RequestMapping(value = "/findAllGroupByPageInfo", method = RequestMethod.POST)
    @ResponseBody
    public ResponsePage findAllGroupByPageInfo(ResponsePage page, HttpServletRequest request) {

        int pageNum = getPageNum();
        int pageSize = getPageSize();
        try {
            Page<GroupBO> groupBOs = groupService.findAllGroups(pageNum, pageSize);
            if (CollectionUtils.isEmpty(groupBOs)) {
                return page.setErrorMsg(FIND_NO_RESULTS);
            }
            return page.setPage(groupBOs, FIND_SUCCESS);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            LOG.error(e.toString());
            return page.setErrorMsg(FIND_NO_RESULTS);
        }

    }

    @RequestMapping(value = "/findGroupRuleByGroupId", method = RequestMethod.POST)
    @ResponseBody
    public ResponsePage findGroupRuleByGroupId(ResponsePage page, Long groupId) {
        try {
            GroupBO groupBO = groupService.findGroupBOById(groupId);
            List<GroupRuleVO> groupRuleVOs = getAllGroupRuleVOS(groupBO);
            if (groupRuleVOs == null || groupRuleVOs.size() == 0) {
                return page.setErrorMsg(FIND_NO_RESULTS);
            }
            return page.setJustList(groupRuleVOs, FIND_SUCCESS);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            LOG.error(e.toString());
            return page.setErrorMsg(FIND_NO_RESULTS);
        }

    }

    private List<GroupRuleVO> getAllGroupRuleVOS(GroupBO groupBO) throws InvocationTargetException,
                                                                  IllegalAccessException {
        List<GroupRuleVO> allGroupRuleVOs = Lists.newArrayList();
        List<GroupRuleBO> singleGroupRuleBOs = groupRuleService.findGroupRuleBOByGroupId(groupBO.getId());
        if (null != singleGroupRuleBOs && !singleGroupRuleBOs.isEmpty()) {
            List<GroupRuleVO> singleGroupRuleVOs = transformRuleBO2VO(singleGroupRuleBOs, groupBO.getGroupName());
            allGroupRuleVOs.addAll(singleGroupRuleVOs);
        }

        return allGroupRuleVOs;
    }

    private List<GroupRuleVO> transformRuleBO2VO(List<GroupRuleBO> groupRuleBOs,
                                                 String groupName) throws InvocationTargetException,
                                                                   IllegalAccessException {
        List<GroupRuleVO> groupRuleVOs = Lists.newArrayList();
        for (GroupRuleBO groupRuleBO : groupRuleBOs) {
            GroupRuleVO groupRuleVO = new GroupRuleVO();
            groupRuleVO.setGroupName(groupName);
            BeanUtils.copyProperties(groupRuleVO, groupRuleBO);
            groupRuleVOs.add(setDataSourceStatus(groupRuleVO));
        }
        return groupRuleVOs;
    }

    private GroupRuleVO setDataSourceStatus(GroupRuleVO groupRuleVO) {
        long dataSourceId = groupRuleVO.getDatasourceId();
        AppDatasourceBO appDatasourceBO = appDatasourceService.findAppDatasourceBOById(dataSourceId);
        Integer status = appDatasourceBO.getSourceStatus();
        if (0 == status) {
            groupRuleVO.setDataSourceStatus("可用");
        } else {
            groupRuleVO.setDataSourceStatus("不可用");
        }
        return groupRuleVO;
    }

    @RequestMapping(value = "/updateGroupRuleInfo", method = RequestMethod.POST)
    @ResponseBody
    public ResponsePage updateGroupRuleInfo(ResponsePage page, HttpServletRequest request, GroupRuleVO groupRuleVO) {
        boolean flag = noticeDiamond(groupRuleVO);
        if (flag) {
            GroupRuleBO groupRuleBO = new GroupRuleBO();
            try {
                BeanUtils.copyProperties(groupRuleBO, groupRuleVO);
                groupRuleService.updateGroupRuleBOById(groupRuleBO);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                LOG.error(e.toString());
            }

        }
        page.setSuccess(true);
        ;
        return page;
    }

    private boolean noticeDiamond(GroupRuleVO groupRuleVO) {
        String groupName = groupRuleVO.getGroupName();
        GroupBO groupBO = groupService.findGroupBOByGroupName(groupName);
        List<GroupRuleBO> groupRuleBOs = groupRuleService.findGroupRuleBOByGroupId(groupBO.getId());
        StringBuffer sbContent = new StringBuffer();
        for (GroupRuleBO groupRuleBO : groupRuleBOs) {
            if (groupRuleVO.getId().equals(groupRuleBO.getId())) {
                sbContent.append(groupRuleBO.getGroupRuleName())
                    .append(":")
                    .append("w")
                    .append(groupRuleVO.getWriteWeight())
                    .append("r")
                    .append(groupRuleVO.getReadWeight())
                    .append(",");
            } else {
                sbContent.append(groupRuleBO.getGroupRuleName())
                    .append(":")
                    .append("w")
                    .append(groupRuleBO.getWriteWeight())
                    .append("r")
                    .append(groupRuleBO.getReadWeight())
                    .append(",");
            }
        }
        String content = sbContent.toString().substring(0, sbContent.toString().lastIndexOf(","));
        String dataId = TGroupDataSource.getFullDbGroupKey(groupRuleVO.getGroupName());
        try {
            gddlShardAppService.updateDiamond(dataId, content);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            LOG.error(e.toString());
            return false;
        }
        return true;
    }

    @RequestMapping(value = "/searchMethod", method = RequestMethod.POST)
    @ResponseBody
    public ResponsePage searchMethod(ResponsePage page, HttpServletRequest request, String type, String keyWord) {
        if ("diamond".equals(type)) {
            findDiamond(page, keyWord);
        } else {
            findGroupBO(page, keyWord);
        }
        return page;
    }

    private void findDiamond(ResponsePage page, String keyWord) {
        String dataId = TGroupDataSource.getFullDbGroupKey(keyWord);
        ConfigInfoBO configInfoBO = configInfoService.findConfigInfoBOByDataId(dataId);
        if (configInfoBO == null) {
            page.setErrorMsg("暂无数据");
        } else {
            page.setSingleObject(configInfoBO, "查询成功");
        }
    }

    private void findGroupBO(ResponsePage page, String keyWord) {
        if (StringUtils.isNotBlank(keyWord)) {
            List<GroupBO> list = groupService.findLikeGroupBOByGroupName(keyWord);
            page.setJustList(list, "查询成功");
        }else{
            Page<GroupBO> paged = groupService.findAllGroups(getPageNum(), getPageSize());
            page.setPage(paged, "查询成功");
        }
    }

    @RequestMapping(value = "/reloadMSInfo", method = RequestMethod.POST)
    @ResponseBody
    public ResponsePage reloadMSInfo(ResponsePage page, HttpServletRequest request, Long configId) {
        try {
            ConfigInfoBO configInfoBO = configInfoService.findConfigInfoBOById(configId);
            proccessGroup(configInfoBO.getDataId(), configInfoBO);
            return page.setSuccessMsg(INSERT_SUCCESS);
        } catch (Exception e) {
            LOG.error(e.toString());
            return page.setErrorMsg(INSERT_FAILURE);
        }
    }

    private void proccessGroup(String dataId, ConfigInfoBO configInfoBO) {
        String groupName = dataId.replaceAll("com.taobao.tddl.jdbc.group_V2.4.1_", "");
        GroupBO groupBO = groupService.findGroupBOByGroupName(groupName);
        if (groupBO == null) {
            groupBO = new GroupBO();
            noGroupProccess(dataId, groupBO, configInfoBO);
        } else {
            haveGroupProccess(dataId, groupBO, configInfoBO);
        }
    }

    private void noGroupProccess(String dataId, GroupBO groupBO, ConfigInfoBO configInfoBO) {
        groupBO.setGroupName(dataId);
        boolean flag = groupService.insertGroupBO(groupBO);
        if (flag) {
            GroupBO bo = groupService.findGroupBOByGroupName(dataId);
            String contnt = configInfoBO.getContent();
            String[] groups = contnt.split(",");
            for (String datasource : groups) {
                String[] rwpqs = StringUtils.split(datasource, ":");
                String groupname = rwpqs[0];
                // 查询ip
                ConfigInfoBO ipBO = configInfoService
                    .findConfigInfoBOByDataId(TAtomConstants.getGlobalDataId(groupname));
                String ipContent = ipBO.getContent();
                Properties globaProp = TAtomConfParser.parserConfStr2Properties(ipContent);
                String ip = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_IP_KEY));
                String port = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_PORT_KEY));
                StringBuffer jdbcUrlBuffer = new StringBuffer();
                jdbcUrlBuffer.append(ip).append(":").append(port);

                AppDatasourceBO appbo = appDatasourceService.findAppDatasourceBOByDbNameAndJdbcUrl(groupname,
                    jdbcUrlBuffer.toString());
                long datasourceId = appbo.getId();

                GroupRuleBO groupRuleBO = new GroupRuleBO();
                groupRuleBO.setDatasourceId(datasourceId);
                groupRuleBO.setGroupId(bo.getId());
                groupRuleBO.setGroupRuleName(groupname);
                if (rwpqs[1].equals("b")) {
                    groupRuleBO.setReadWeight(0);
                    groupRuleBO.setReadPriority(0);
                    groupRuleBO.setWritePriority(0);
                    groupRuleBO.setWriteWeight(0);
                    groupRuleBO.setIsBak(1);
                } else {
                    Weight weight = new Weight(rwpqs[1]);
                    groupRuleBO.setReadWeight(weight.r);
                    groupRuleBO.setReadPriority(weight.p);
                    groupRuleBO.setWritePriority(weight.q);
                    groupRuleBO.setWriteWeight(weight.w);
                    groupRuleBO.setIsBak(0);
                }
                groupRuleService.insertGroupRuleBO(groupRuleBO);
            }
        }

    }

    private void haveGroupProccess(String dataId, GroupBO groupBO, ConfigInfoBO configInfoBO) {
        String contnt = configInfoBO.getContent();
        String[] groups = contnt.split(",");
        for (String datasource : groups) {
            String[] rwpqs = StringUtils.split(datasource, ":");
            String groupname = rwpqs[0];
            GroupRuleBO groupRuleBO = groupRuleService.findGroupRuleBOByIdAndName(groupBO.getId(), groupname);
            if (groupRuleBO == null) {
                groupRuleBO = new GroupRuleBO();
                groupRuleBO.setDatasourceId(getAppDatasourceId(groupname, groupBO.getAppId()));
                groupRuleBO.setGroupId(groupBO.getId());
                groupRuleBO.setGroupRuleName(groupname);
                if (rwpqs[1].equals("b")) {
                    groupRuleBO.setReadWeight(0);
                    groupRuleBO.setReadPriority(0);
                    groupRuleBO.setWritePriority(0);
                    groupRuleBO.setWriteWeight(0);
                    groupRuleBO.setIsBak(1);
                } else {
                    Weight weight = new Weight(rwpqs[1]);
                    groupRuleBO.setReadWeight(weight.r);
                    groupRuleBO.setReadPriority(weight.p);
                    groupRuleBO.setWritePriority(weight.q);
                    groupRuleBO.setWriteWeight(weight.w);
                    groupRuleBO.setIsBak(0);
                }
                groupRuleService.insertGroupRuleBO(groupRuleBO);
            } else {
                groupRuleBO.setGroupId(groupBO.getId());
                groupRuleBO.setGroupRuleName(groupname);
                if (rwpqs[1].equals("b")) {
                    groupRuleBO.setReadWeight(0);
                    groupRuleBO.setReadPriority(0);
                    groupRuleBO.setWritePriority(0);
                    groupRuleBO.setWriteWeight(0);
                    groupRuleBO.setIsBak(1);
                } else {
                    Weight weight = new Weight(rwpqs[1]);
                    groupRuleBO.setReadWeight(weight.r);
                    groupRuleBO.setReadPriority(weight.p);
                    groupRuleBO.setWritePriority(weight.q);
                    groupRuleBO.setWriteWeight(weight.w);
                    groupRuleBO.setIsBak(0);
                }
                groupRuleService.updateGroupRuleBOById(groupRuleBO);
            }

        }
    }

    private long getAppDatasourceId(String dataId, Long appId) {

        ConfigInfoBO ipBO = configInfoService.findConfigInfoBOByDataId(TAtomConstants.getGlobalDataId(dataId));
        String ipContent = ipBO.getContent();
        Properties globaProp = TAtomConfParser.parserConfStr2Properties(ipContent);
        String ip = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_IP_KEY));
        String port = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_PORT_KEY));
        String dbType = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_DB_TYPE_KEY));
        String dbStatus = TStringUtil.trim(globaProp.getProperty(TAtomConfParser.GLOBA_DB_STATUS_KEY));
        StringBuffer jdbcUrlBuffer = new StringBuffer();
        jdbcUrlBuffer.append(ip).append(":").append(port);

        AppDatasourceBO appbo = appDatasourceService.findAppDatasourceBOByDbNameAndJdbcUrl(dataId,
            jdbcUrlBuffer.toString());

        if (appbo == null) {
            appbo = new AppDatasourceBO();

            String source_type = "mysql".equals(dbType) ? "1" : "3";
            String jdbcUrl = "mysql".equals(dbType) ? "jdbc:mysql://" + jdbcUrlBuffer.toString() + "/"
                                                      + dataId : "jdbc:oracle:thin:@" + jdbcUrlBuffer.toString() + ":"
                                                                 + dataId;
            DatasourceBO datasourceBO = datasourceService.findDatasourceBOByTypeAndUrl(source_type, jdbcUrl);
            appbo.setDbName(dataId);
            appbo.setJdbcUrl(jdbcUrlBuffer.toString());
            appbo.setAppId(appId);
            appbo.setSourceType(dbType);
            appbo.setPassword(datasourceBO.getPassword());
            appbo.setUserName(datasourceBO.getUserName());
            appbo.setSourceType("RW".equals(dbStatus) ? "0" : "1");
            appDatasourceService.saveAppDatasourceBO(appbo);
            appbo = appDatasourceService.findAppDatasourceBOByDbNameAndJdbcUrl(dataId, jdbcUrlBuffer.toString());
            long datasourceId = appbo.getId();

            return datasourceId;

        } else {
            long datasourceId = appbo.getId();

            return datasourceId;
        }

    }
}
